#include "lwip/opt.h"
#include "stm32f4xx_eth_bsp.h"
#include "main.h"
#include "stm32f4xx_eth_conf.h"
#include "stm32f4xx_eth.h"
#include "misc.h"

#include "stm32f429xx.h"

#include "netif.h"
#include "netconf.h"
#include "lwip/dhcp.h"
/* Scheduler includes */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "usb_configure.h"
#include "globals.h"
#include "stm32f4xx_syscfg.h"

#define ETH_LINK_TASK_STACK_SIZE			( configMINIMAL_STACK_SIZE * 2 )
#define ETH_LINK_TASK_PRIORITY		        ( tskIDLE_PRIORITY )
#define emacBLOCK_TIME_WAITING_ETH_LINK_IT	( ( portTickType ) 100 )

ETH_InitTypeDef ETH_InitStructure;
__IO uint32_t  EthStatus = 0;
struct netif xnetif;
__IO uint8_t DHCP_state;

static void ETH_MACDMA_Config(void);
static void ETH_GPIO_Config(void);
static void ETH_NVIC_Config(void);


void ETH_BSP_Config(void)
{
	/* Configure the GPIO ports for ethernet pins */
	ETH_GPIO_Config();

	/* Config NVIC for Ethernet */
	ETH_NVIC_Config();

	/* Configure the Ethernet MAC/DMA */
	ETH_MACDMA_Config();

	/* Get Ethernet link status*/
	if((ETH_ReadPHYRegister(ETH_PHY_ADDRESS, PHY_BSR)) & PHY_Linked_Status)
	{
		EthStatus |= ETH_LINK_FLAG;
		/* create the task that handles the ETH_link */
		xTaskCreate(Eth_Link_IT_task, (signed char*) "E_link", ETH_LINK_TASK_STACK_SIZE, (void *)ETH_PHY_ADDRESS,
				ETH_LINK_TASK_PRIORITY,NULL);
	}
}

/**
 * @brief  Configures the Ethernet Interface
 * @param  None
 * @retval None
 */
static void ETH_MACDMA_Config(void)
{
	/* Enable ETHERNET clock  */
	RCC->AHB1ENR |= ( RCC_AHB1ENR_ETHMACEN | RCC_AHB1ENR_ETHMACTXEN | RCC_AHB1ENR_ETHMACRXEN );

	/* Reset ETHERNET on AHB Bus */
	ETH_DeInit();

	/* Software reset */
	ETH_SoftwareReset();

	/* Wait for software reset */
	while (ETH_GetSoftwareResetStatus() == SET);

	/* ETHERNET Configuration --------------------------------------------------*/
	/* Call ETH_StructInit if you don't like to configure all ETH_InitStructure parameter */
	ETH_StructInit(&ETH_InitStructure);

	/* Fill ETH_InitStructure parametrs */
	/*------------------------   MAC   -----------------------------------*/
	ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable;
	ETH_InitStructure.ETH_LoopbackMode = ETH_LoopbackMode_Disable;
	ETH_InitStructure.ETH_RetryTransmission = ETH_RetryTransmission_Disable;
	ETH_InitStructure.ETH_AutomaticPadCRCStrip = ETH_AutomaticPadCRCStrip_Disable;
	ETH_InitStructure.ETH_ReceiveAll = ETH_ReceiveAll_Disable;
	ETH_InitStructure.ETH_BroadcastFramesReception = ETH_BroadcastFramesReception_Enable;
	ETH_InitStructure.ETH_PromiscuousMode = ETH_PromiscuousMode_Disable;
	ETH_InitStructure.ETH_MulticastFramesFilter = ETH_MulticastFramesFilter_Perfect;
	ETH_InitStructure.ETH_UnicastFramesFilter = ETH_UnicastFramesFilter_Perfect;
#ifdef CHECKSUM_BY_HARDWARE
	ETH_InitStructure.ETH_ChecksumOffload = ETH_ChecksumOffload_Enable;
#endif

	/*------------------------   DMA   -----------------------------------*/

	/* When we use the Checksum offload feature, we need to enable the Store and Forward mode:
  the store and forward guarantee that a whole frame is stored in the FIFO, so the MAC can insert/verify the checksum,
  if the checksum is OK the DMA can handle the frame otherwise the frame is dropped */
	ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame = ETH_DropTCPIPChecksumErrorFrame_Enable;
	ETH_InitStructure.ETH_ReceiveStoreForward = ETH_ReceiveStoreForward_Enable;
	ETH_InitStructure.ETH_TransmitStoreForward = ETH_TransmitStoreForward_Enable;

	ETH_InitStructure.ETH_ForwardErrorFrames = ETH_ForwardErrorFrames_Disable;
	ETH_InitStructure.ETH_ForwardUndersizedGoodFrames = ETH_ForwardUndersizedGoodFrames_Disable;
	ETH_InitStructure.ETH_SecondFrameOperate = ETH_SecondFrameOperate_Enable;
	ETH_InitStructure.ETH_AddressAlignedBeats = ETH_AddressAlignedBeats_Enable;
	ETH_InitStructure.ETH_FixedBurst = ETH_FixedBurst_Enable;
	ETH_InitStructure.ETH_RxDMABurstLength = ETH_RxDMABurstLength_32Beat;
	ETH_InitStructure.ETH_TxDMABurstLength = ETH_TxDMABurstLength_32Beat;
	ETH_InitStructure.ETH_DMAArbitration = ETH_DMAArbitration_RoundRobin_RxTx_2_1;

	/* Configure Ethernet */
	EthStatus = ETH_Init(&ETH_InitStructure, ETH_PHY_ADDRESS);

	/* Enable the Ethernet Rx Interrupt */
	ETH_DMAITConfig(ETH_DMA_IT_NIS | ETH_DMA_IT_R, ENABLE);

	/* Enabled interrupt PHY changed link */
	ETH_WritePHYRegister(ETH_PHY_ADDRESS, PHY_INTERRUPT_REG, ( PHY_INTERRUPT_EN_LINK_DOWN | PHY_INTERRUPT_EN_LINK_UP ));
}

/**
 * @brief  Configures the different GPIO ports.
 * @param  None
 * @retval None
 */
void ETH_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	/* Enable GPIOs clocks */
	__HAL_RCC_GPIOA_CLK_ENABLE();
	__HAL_RCC_GPIOB_CLK_ENABLE();
	__HAL_RCC_GPIOC_CLK_ENABLE();
	__HAL_RCC_GPIOD_CLK_ENABLE();
	__HAL_RCC_GPIOE_CLK_ENABLE();
	__HAL_RCC_GPIOF_CLK_ENABLE();
	__HAL_RCC_GPIOG_CLK_ENABLE();
	/* Enable SYSCFG clock */
	__HAL_RCC_SYSCFG_CLK_ENABLE();
	SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_RMII);
	/* RMII_REF_CLK */
	GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
	GPIO_InitStructure.Pin = ETH_RMII_REF_CLK_PIN;
	GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStructure.Pull = GPIO_PULLUP;
	GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
	HAL_GPIO_Init(ETH_RMII_REF_CLK_PORT, &GPIO_InitStructure);
	/* MDIO */
	GPIO_InitStructure.Pin = ETH_MDIO_PIN;
	HAL_GPIO_Init(ETH_MDIO_PORT, &GPIO_InitStructure);
	/* ETH_RMII_CRS_DV */
	GPIO_InitStructure.Pin = ETH_RMII_CRS_DV_PIN;
	GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
	HAL_GPIO_Init(ETH_RMII_CRS_DV_PORT, &GPIO_InitStructure);
	/* ETH_MDC */
	GPIO_InitStructure.Pin = ETH_MDC_PIN;
	GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
	HAL_GPIO_Init(ETH_MDC_PORT, &GPIO_InitStructure);
	/* ETH_RMII_RXD0_PORT */
	GPIO_InitStructure.Pin = ETH_RMII_RXD0_PIN;
	GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
	HAL_GPIO_Init(ETH_RMII_RXD0_PORT, &GPIO_InitStructure);
	/* ETH_RMII_RXD1_PORT */
	GPIO_InitStructure.Pin = ETH_RMII_RXD1_PIN;
	GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
	HAL_GPIO_Init(ETH_RMII_RXD1_PORT, &GPIO_InitStructure);
	/* ETH_MDC */
	GPIO_InitStructure.Pin = ETH_RMII_TX_EN_PIN;
	GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
	HAL_GPIO_Init(ETH_RMII_TX_EN_PORT, &GPIO_InitStructure);
	/* ETH_MDC */
	GPIO_InitStructure.Pin = ETH_RMII_TXD0_PIN;
	GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
	HAL_GPIO_Init(ETH_RMII_TXD0_PORT, &GPIO_InitStructure);
	/* ETH_MDC */
	GPIO_InitStructure.Pin = ETH_RMII_TXD1_PIN;
	GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
	HAL_GPIO_Init(ETH_RMII_TXD1_PORT, &GPIO_InitStructure);
	/* Configure reset PHY */
	GPIO_InitStructure.Pin = ETH_RESET_PHY_PIN;
	GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
	HAL_GPIO_Init(ETH_RESET_PHY_PORT, &GPIO_InitStructure);
	HAL_GPIO_WritePin(ETH_RESET_PHY_PORT, ETH_RESET_PHY_PIN, GPIO_PIN_RESET);
	_delay(1000);
	HAL_GPIO_WritePin(ETH_RESET_PHY_PORT, ETH_RESET_PHY_PIN, GPIO_PIN_SET);
	/* INT PHY */
	GPIO_InitStructure.Pin = ETH_LINK_PIN;
	GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
	GPIO_InitStructure.Pull = GPIO_PULLUP;
	HAL_GPIO_Init(ETH_LINK_GPIO_PORT, &GPIO_InitStructure);
}

/**
 * @brief  Configures and enable the Ethernet global interrupt.
 * @param  None
 * @retval None
 */
void ETH_NVIC_Config(void)
{
	NVIC_InitTypeDef   NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = ETH_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 12;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

/**
 * @brief  This function handles Ethernet link status.
 * @param  None
 * @retval None
 */
void Eth_Link_IT_task( void * pvParameters )
{
	uint32_t pcPHYAddress;
	pcPHYAddress = ( uint32_t  ) pvParameters;

	for(;;)
	{
		if(!(ETH_LINK_GPIO_PORT->IDR & ETH_LINK_PIN))
		{
			/* Clear Interrupt reg */
			ETH_ReadPHYRegister(pcPHYAddress, PHY_INTERRUPT_REG);

			if((ETH_ReadPHYRegister((uint16_t) pcPHYAddress, PHY_SR )) & PHY_Linked_Status)
				netif_set_link_up(&xnetif);
			else
				netif_set_link_down(&xnetif);
		}
		vTaskDelay(5000);
	}
}
/**
 * @brief  Link callback function, this function is called on change of link status.
 * @param  The network interface
 * @retval None
 */
void ETH_link_callback(struct netif *netif)
{
	struct ip_addr ipaddr;
	struct ip_addr netmask;
	struct ip_addr gw;

	__IO uint32_t timeout = 0;
	uint32_t tmpreg,RegValue;

	if(netif_is_link_up(netif))
	{
		/* Restart the autonegotiation */
		if(ETH_InitStructure.ETH_AutoNegotiation != ETH_AutoNegotiation_Disable)
		{
			/* Reset Timeout counter */
			timeout = 0;

			/* Enable Auto-Negotiation */
			ETH_WritePHYRegister(ETH_PHY_ADDRESS, PHY_BCR, PHY_AutoNegotiation);

			/* Wait until the auto-negotiation will be completed */
			do
			{
				timeout++;
			} while (!(ETH_ReadPHYRegister(ETH_PHY_ADDRESS, PHY_BSR) & PHY_AutoNego_Complete) && (timeout < (uint32_t)PHY_READ_TO));

			/* Reset Timeout counter */
			timeout = 0;

			/* Read the result of the auto-negotiation */
			RegValue = ETH_ReadPHYRegister(ETH_PHY_ADDRESS, PHY_SR);

			ETH_InitStructure.ETH_Mode = ETH_Mode_HalfDuplex;
			ETH_InitStructure.ETH_Speed = ETH_Speed_10M;

			if( RegValue & PHY_USED_100BASE_FULL_DUPLEX )
			{
				ETH_InitStructure.ETH_Mode = ETH_Mode_FullDuplex;
				ETH_InitStructure.ETH_Speed = ETH_Speed_100M;
			}
			else
			{
				if( RegValue & PHY_USED_100BASE_HALF_DUPLEX )
				{
					ETH_InitStructure.ETH_Mode = ETH_Mode_HalfDuplex;
					ETH_InitStructure.ETH_Speed = ETH_Speed_100M;
				}
				else
				{
					if( RegValue & PHY_USED_10BASE_FULL_DUPLEX )
					{
						ETH_InitStructure.ETH_Mode = ETH_Mode_FullDuplex;
						ETH_InitStructure.ETH_Speed = ETH_Speed_10M;
					}else
					{
						if( RegValue & PHY_USED_10BASE_HALF_DUPLEX )
						{
							ETH_InitStructure.ETH_Mode = ETH_Mode_HalfDuplex;
							ETH_InitStructure.ETH_Speed = ETH_Speed_10M;
						}
					}
				}
			}
			/*------------------------ ETHERNET MACCR Re-Configuration --------------------*/
			/* Get the ETHERNET MACCR value */
			tmpreg = ETH->MACCR;

			/* Set the FES bit according to ETH_Speed value */
			/* Set the DM bit according to ETH_Mode value */
			tmpreg |= (uint32_t)(ETH_InitStructure.ETH_Speed | ETH_InitStructure.ETH_Mode);

			/* Write to ETHERNET MACCR */
			ETH->MACCR = (uint32_t)tmpreg;

			_delay(ETH_REG_WRITE_DELAY);
			tmpreg = ETH->MACCR;
			ETH->MACCR = tmpreg;
		}

		/* Restart MAC interface */
		ETH_Start();

		if(startParam.net.net_use & E_NETWORK_USE_DHCP)
		{
			ipaddr.addr = 0;
			netmask.addr = 0;
			gw.addr = 0;
			DHCP_state = DHCP_START;
		}else
		{
			IP4_ADDR(&ipaddr, startParam.net.ip_addr[0], startParam.net.ip_addr[1],
					startParam.net.ip_addr[2], startParam.net.ip_addr[3]);
			IP4_ADDR(&netmask, startParam.net.sub[0], startParam.net.sub[1],
					startParam.net.sub[2], startParam.net.sub[3]);
			IP4_ADDR(&gw, startParam.net.gw_addr[0], startParam.net.gw_addr[1],
					startParam.net.gw_addr[2], startParam.net.gw_addr[3]);
		}
		netif_set_addr(&xnetif, &ipaddr, &netmask, &gw);

		/* When the netif is fully configured this function must be called.*/
		netif_set_up(&xnetif);
	}
	else
	{
		ETH_Stop();
		if(startParam.net.net_use & E_NETWORK_USE_DHCP)
		{
			DHCP_state = DHCP_LINK_DOWN;
			dhcp_stop(netif);
		}

		/*  When the netif link is down this function must be called.*/
		netif_set_down(&xnetif);
	}
}
